﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Reactive.Linq;
using System.ServiceModel;
using System.ServiceModel.Reactive;
using DaveSexton.Labs;
using Rxx.Labs.Properties;

namespace Rxx.Labs.Reactive.Networking
{
	[DisplayName("WCF (New)")]
	[Description("Returning IObservable<T> from WCF services.")]
	public sealed partial class WcfLab : BaseConsoleLab
	{
		private const string serviceUrl = "http://localhost:48588/";

		protected override void Main()
		{
			RunExperiments();
		}

		[Experiment("Sync Client, Async Service")]
		[Description("Invokes a service operation synchronously on the client and "
							 + "asynchronously on the server.")]
		public void SynchronousClientExperiment()
		{
			TraceLine(Instructions.WaitForCompletion);

			// ExampleServiceClient is auto-generated by Visual Studio.
			using (var client = new ExampleServiceClient(
				new BasicHttpBinding(),
				new EndpointAddress(serviceUrl)))
			{
				var results = client.GetHttpResponses(new[]
				{
					new Uri("http://microsoft.com"),
					new Uri("http://codeplex.com"),
					new Uri("http://google.com"),
					new Uri("http://wikipedia.org")
				});

				foreach (var result in results)
				{
					TraceLine();
					TraceSuccess(result.Url.ToString());
					TraceLine(result.ResponseSummary);
				}
			}
		}

		[Experiment("Async Client, Async Service")]
		[Description("Invokes a service operation asynchronously on the client and the server.")]
		public void AsynchronousClientExperiment()
		{
			TraceLine(Instructions.PressAnyKeyToCancel);
			TraceLine();

			// ExampleServiceClient is auto-generated by Visual Studio.
			using (var client = new ExampleServiceClient(
				new BasicHttpBinding(),
				new EndpointAddress(serviceUrl)))
			{
				var invokeAsync = Observable.FromAsyncPattern<Uri[], ExampleServiceResponseReference>(
					client.BeginGetSlowestHttpResponse,
					client.EndGetSlowestHttpResponse);

				var observable = invokeAsync(new[]
					{
						new Uri("http://microsoft.com"),
						new Uri("http://codeplex.com"),
						new Uri("http://google.com"),
						new Uri("http://wikipedia.org")
					});

				using (observable.Subscribe(
					result =>
					{
						TraceSuccess(result.Url.ToString());
						TraceLine(result.ResponseSummary);
						TraceLine();
					},
					ConsoleOutputOnError(),
					ConsoleOutputOnCompleted()))
				{
					WaitForKey();
				}
			}
		}
	}

	[ServiceContract, ObservableService]
	public interface IExampleService
	{
		[OperationContract, ObservableOperation(ReturnAsList = true)]
		IObservable<ExampleServiceResponse> GetHttpResponses(params Uri[] urls);

		[OperationContract]
		IObservable<ExampleServiceResponse> GetSlowestHttpResponse(params Uri[] urls);
	}

	public sealed class ExampleService : IExampleService
	{
		public IObservable<ExampleServiceResponse> GetHttpResponses(params Uri[] urls)
		{
			return urls
				.Select(url => ObservableWebClient
					.DownloadString(url)
					.Select(response => new ExampleServiceResponse()
					{
						Url = url,
						ResponseSummary = response.Substring(0, Math.Min(response.Length, 100))
					}))
				.Merge();
		}

		public IObservable<ExampleServiceResponse> GetSlowestHttpResponse(params Uri[] urls)
		{
			return GetHttpResponses(urls);
		}
	}

	public sealed class ExampleServiceResponse
	{
		public Uri Url { get; set; }
		public string ResponseSummary { get; set; }
	}
}